#if defined(_WIN32)

#define		FD_SETSIZE	4096
#include	<WinSock2.h>
#include	<WS2tcpip.h>
#include	<Network.h>
#pragma		comment(lib, "ws2_32.lib")

//////////////////////////////////////////////////////////////////////////
/// Win32 Socket wrapper.
//////////////////////////////////////////////////////////////////////////
class Socket : public ISocket {
public:
	Socket();
	virtual ~Socket();

	virtual int			Connect(const std::string & sIP, int nPort) override;
	virtual int			Listen(const std::string & sIP, int nPort) override;
	virtual ISocket *	Accept(uint32_t & nIP) override;
	virtual int			Recv(char * pBuf, size_t nSize) override;
	virtual bool		Send(const char * pData, size_t nSize) override;
	virtual void		Close(int nReason) override;

private:
	SOCKET _nSocket;
};

//////////////////////////////////////////////////////////////////////////
/// IMPLEMENT Global network interfaces.
//////////////////////////////////////////////////////////////////////////
namespace Network {

	fd_set GNetSocketSet = { 0 };
	fd_set GNetSocketCopy = { 0 };
	struct timeval GNetSocketWait = { 0, 0 };
	std::map<SOCKET, Socket *> GNetSocketListener;

	ISocket * CreatePlatformSocket() {
		return new Socket();
	}

	void Register(SOCKET nSocket, Socket * pListener) {
		auto it = GNetSocketListener.find(nSocket);
		if (it != GNetSocketListener.end()) GNetSocketListener.erase(it);
		if (!FD_ISSET(nSocket, &GNetSocketSet)) FD_SET(nSocket, &GNetSocketSet);
		GNetSocketListener[nSocket] = pListener;
	}

	void Unregister(SOCKET nSocket) {
		if (FD_ISSET(nSocket, &GNetSocketSet)) FD_CLR(nSocket, &GNetSocketSet);
		auto it = GNetSocketListener.find(nSocket);
		if (it != GNetSocketListener.end()) GNetSocketListener.erase(it);
	}

	void Breath() {
		if (GNetSocketSet.fd_count == 0) return;
		memcpy(&GNetSocketCopy, &GNetSocketSet, sizeof(GNetSocketCopy));

		int nCount = select(0, &GNetSocketCopy, 0, 0, &GNetSocketWait);
		if (nCount <= 0) return;

		for (u_int n = 0; n < GNetSocketCopy.fd_count; ++n) {
			SOCKET nSocket = GNetSocketCopy.fd_array[n];
			auto it = GNetSocketListener.find(nSocket);
			if (it != GNetSocketListener.end()) {
				Socket * pSocket = it->second;
				if (pSocket->pListener) pSocket->pListener->OnProcess(NET_EVENT_READABLE, 0);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
/// IMPLEMENT Win32Socket
//////////////////////////////////////////////////////////////////////////

Socket::Socket() : _nSocket(INVALID_SOCKET) {
	WSADATA wOut;
	if (WSAStartup(MAKEWORD(2, 2), &wOut)) throw std::runtime_error("WSAStartup failed!!!");
}

Socket::~Socket() {
	Close(NET_CLOSE_LOCAL);
	WSACleanup();
}

int Socket::Connect(const std::string & sIP, int nPort) {
	if (_nSocket != INVALID_SOCKET) return NET_ERR_RUNNING;

	struct sockaddr_in iAddr;
	memset(&iAddr, 0, sizeof(iAddr));

	iAddr.sin_family = AF_INET;
	iAddr.sin_port = htons(nPort);

	if (0 >= inet_pton(AF_INET, sIP.c_str(), &iAddr.sin_addr)) return NET_ERR_ADDR;

	_nSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (_nSocket == INVALID_SOCKET) return WSAGetLastError();

	u_long nFlags = 1;
	if (ioctlsocket(_nSocket, FIONBIO, &nFlags) != 0) {
		closesocket(_nSocket);
		_nSocket = INVALID_SOCKET;
		return WSAGetLastError();
	}

	if (connect(_nSocket, (sockaddr *)&iAddr, sizeof(sockaddr)) < 0) {
		int nErr = WSAGetLastError();
		if (nErr != WSAEWOULDBLOCK) {
			closesocket(_nSocket);
			_nSocket = INVALID_SOCKET;
			return nErr;
		}

		fd_set iSet;
		struct timeval iWait;

		iWait.tv_sec = 1;
		iWait.tv_usec = 0;

		FD_ZERO(&iSet);
		FD_SET(_nSocket, &iSet);

		nErr = select(0, 0, &iSet, 0, &iWait);
		if (nErr <= 0) {
			closesocket(_nSocket);
			_nSocket = INVALID_SOCKET;
			return nErr == 0 ? NET_ERR_TIMEOUT : WSAGetLastError();
		}
	}

	Network::Register(_nSocket, this);
	if (pListener) pListener->OnProcess(NET_EVENT_CONNECTED, 0);
	return 0;
}

int Socket::Listen(const std::string & sIP, int nPort) {
	if (_nSocket != INVALID_SOCKET) return NET_ERR_RUNNING;

	struct sockaddr_in iAddr;
	memset(&iAddr, 0, sizeof(iAddr));

	iAddr.sin_family = AF_INET;
	iAddr.sin_port = htons(nPort);

	if (0 >= inet_pton(AF_INET, sIP.c_str(), &iAddr.sin_addr)) return NET_ERR_ADDR;

	_nSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (_nSocket == INVALID_SOCKET) return WSAGetLastError();

	u_long nFlags = 1;
	if (ioctlsocket(_nSocket, FIONBIO, &nFlags) != 0) {
		closesocket(_nSocket);
		_nSocket = INVALID_SOCKET;
		return WSAGetLastError();
	}

	if (::bind(_nSocket, (sockaddr *)&iAddr, sizeof(sockaddr)) < 0 || ::listen(_nSocket, FD_SETSIZE) < 0) {
		closesocket(_nSocket);
		_nSocket = INVALID_SOCKET;
		return WSAGetLastError();
	}

	Network::Register(_nSocket, this);
	return 0;
}

ISocket * Socket::Accept(uint32_t & nIP) {
	sockaddr_in iAddr = { 0 };
	int nSizeOfAddr = sizeof(iAddr);
	SOCKET nAccept = accept(_nSocket, (sockaddr *)&iAddr, &nSizeOfAddr);
	if (nAccept == INVALID_SOCKET) return nullptr;

	u_long nFlag = 1;
	if (ioctlsocket(nAccept, FIONBIO, &nFlag) != 0) {
		closesocket(nAccept);
		return nullptr;
	}

	Socket * pAccept = new Socket;
	pAccept->_nSocket = nAccept;
	Network::Register(nAccept, pAccept);

	nIP = iAddr.sin_addr.s_addr;
	return pAccept;
}

int Socket::Recv(char * pBuf, size_t nSize) {
	if (_nSocket == INVALID_SOCKET) return NET_RECV_CLOSED;

	int nRecv = recv(_nSocket, pBuf, (int)nSize, 0);
	if (nRecv > 0) return nRecv;
	if (nRecv < 0 && WSAGetLastError() == WSAEWOULDBLOCK) return NET_RECV_PENDING;
	return nRecv == 0 ? NET_RECV_CLOSED : NET_RECV_ERROR;
}

bool Socket::Send(const char * pData, size_t nSize) {
	if (_nSocket == INVALID_SOCKET) return false;

	char *	pSend = (char *)pData;
	int		nSend = 0;
	int		nLeft = (int)nSize;

	while (true) {
		nSend = send(_nSocket, pSend, nLeft, 0);
		if (nSend < 0) {
			int nErr = WSAGetLastError();
			if (nErr == WSAEWOULDBLOCK) {
				Sleep(1);
			} else {
				return false;
			}
		} else if (nSend < nLeft) {
			nLeft -= nSend;
			pSend += nSend;
		} else if (nSend == nLeft) {
			return true;
		} else {
			return nLeft == 0;
		}
	}
}

void Socket::Close(int nReason) {
	if (_nSocket == INVALID_SOCKET) return;
	Network::Unregister(_nSocket);
	closesocket(_nSocket);
	_nSocket = INVALID_SOCKET;
	if (pListener) pListener->OnProcess(NET_EVENT_CLOSED, nReason);
}

#endif